/*
 * DevicesLed.c
 *
 *      Author: Honrun
 */

#include "gd32f30x.h"
#include "string.h"
#include "DevicesLed.h"



static LedInfoType st_typeLedInfo;

/* 周期调用计数（调用频率为10k） */
volatile static uint32_t st_uiLedTickCnt = 0;
volatile static uint32_t st_uiLedSosCnt = 0;
/* 呼吸档位 */
volatile static int16_t st_sGrade = 0;



void vLedInit(void)
{
    /* enable the LED GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOB);

    /* configure LED GPIO pin */
    gpio_init(LED_BLUE_GPIO_Port,  GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, LED_BLUE_Pin);


    st_typeLedInfo.blue.driveMode       = LED_DRIVE_IO;
    st_typeLedInfo.blue.channel         = LED_CHANNEL_BLUE;


    vLedSetStatusFlashSlow(LED_CHANNEL_ALL);
}

void vLedOpen(uint32_t uiChannel)
{
    if(uiChannel & LED_CHANNEL_BLUE)
        gpio_bit_set(LED_BLUE_GPIO_Port, LED_BLUE_Pin);
}

void vLedClose(uint32_t uiChannel)
{
    if(uiChannel & LED_CHANNEL_BLUE)
        gpio_bit_reset(LED_BLUE_GPIO_Port, LED_BLUE_Pin);
}

void vLedRevesal(uint32_t uiChannel)
{
    if(uiChannel & LED_CHANNEL_BLUE)
        GPIO_OCTL(LED_BLUE_GPIO_Port) ^= LED_BLUE_Pin;
}

static void vLedDutySet(LedType *ptypeLed, float fDuty)
{
    if(ptypeLed->driveMode == LED_DRIVE_NULL)
        return;

    /* 限制占空比在： 0% - 100% */
    fDuty = (fDuty < 0.0f) ? 0.0f : ((fDuty > 1.0f) ? 1.0f : fDuty);

    /* PWM 模式 */
    if(ptypeLed->driveMode == LED_DRIVE_PWM)
    {
        /* fDuty: 百分比（0.0f - 1.0f） */
        timer_channel_output_pulse_value_config(ptypeLed->periph, ptypeLed->channel, TIMER_CAR(ptypeLed->periph) * fDuty);
    }
    /* 普通IO 模式 */
    else
    {
        (fDuty <= 0.0f) ? vLedClose(ptypeLed->channel) : vLedOpen(ptypeLed->channel);
    }
}

/*
 * Return:      void
 * Parameters:  usChannel: LED通道; usChannel: 状态; usChannel: 闪烁次数/或者占空比;
 * Description: 设置LED状态
 */
void vLedSetStatus(LedChannelEnum usChannel, LedStateEnum enumStatus, uint8_t ucFlashCnt_or_Duty)
{
    if(usChannel & LED_CHANNEL_BLUE)
    {
        st_typeLedInfo.blue.state = enumStatus;
        st_typeLedInfo.blue.flashCnt = ucFlashCnt_or_Duty * 2 + 1;
        st_typeLedInfo.blue.duty = ucFlashCnt_or_Duty % 101;
    }
}

/*
 * Return:      void
 * Parameters:  *ptypeLed: LED信息结构体
 * Description: LED状态刷新
 */
static void vLedStateMachine(LedType *ptypeLed)
{
    switch(ptypeLed->state)
    {
        /* 关闭 */
        case LED_DISABLE:
            vLedDutySet(ptypeLed, 0.0f);
            ptypeLed->state = LED_IDLE;
            break;

        /* 常亮 */
        case LED_ENABLE:
            vLedDutySet(ptypeLed, LED_HIGH_DUTY);
            ptypeLed->state = LED_IDLE;
            break;

        /* 常亮 - 低亮 */
        case LED_ENABLE_LOW:
            vLedDutySet(ptypeLed, LED_DIM_DUTY);
            ptypeLed->state = LED_IDLE;
            break;

        /* 呼吸 */
        case LED_BREATHE:
            /* 符合人类生理的呼吸曲线：y = x * x */
            vLedDutySet(ptypeLed, (st_sGrade < 0) ? 0.0f : (st_sGrade * st_sGrade * 0.000064f));
            break;

        /* 固定占空比 */
        case LED_DUTY:
            vLedDutySet(ptypeLed, ptypeLed->duty / 100.0f);
            ptypeLed->state = LED_IDLE;
            break;

        /* 快速频率 持续闪烁/闪烁后关闭/闪烁后常亮 */
        case LED_FLASH_FAST:
        case LED_FLASH_FAST_ENABLE_CNT:
        case LED_FLASH_FAST_DISABLE_CNT:
            if((st_uiLedTickCnt % (LED_FLASH_FAST_PERIOD / 2)) == 0)
            {
                vLedDutySet(ptypeLed, ((st_uiLedTickCnt / (LED_FLASH_FAST_PERIOD / 2)) & 1) ? 1.0f : 0.0f);

                if((ptypeLed->state != LED_FLASH_FAST) && ((ptypeLed->flashCnt--) <= 0))
                {
                    ptypeLed->state = (ptypeLed->state == LED_FLASH_FAST_ENABLE_CNT) ? LED_ENABLE : LED_DISABLE;
                }
            }
            break;

        /* 低速频率 持续闪烁/闪烁后关闭/闪烁后常亮 */
        case LED_FLASH_SLOW:
        case LED_FLASH_SLOW_ENABLE_CNT:
        case LED_FLASH_SLOW_DISABLE_CNT:
            if((st_uiLedTickCnt % (LED_FLASH_SLOW_PERIOD / 2)) == 0)
            {
                vLedDutySet(ptypeLed, ((st_uiLedTickCnt / (LED_FLASH_SLOW_PERIOD / 2)) & 1) ? 1.0f : 0.0f);

                if((ptypeLed->state != LED_FLASH_SLOW) && ((ptypeLed->flashCnt--) <= 0))
                {
                    ptypeLed->state = (ptypeLed->state == LED_FLASH_SLOW_ENABLE_CNT) ? LED_ENABLE : LED_DISABLE;
                }
            }
            break;

        /* 快闪3次，再慢闪3次，依次循环 */
        case LED_FLASH_SOS:
            if(st_uiLedSosCnt < (LED_FLASH_FAST_PERIOD * 3))
            {
                vLedDutySet(ptypeLed, ((st_uiLedSosCnt / (LED_FLASH_FAST_PERIOD / 2)) & 1) ? 1.0f : 0.0f);
            }
            else if(st_uiLedSosCnt < ((LED_FLASH_FAST_PERIOD + LED_FLASH_SLOW_PERIOD) * 3))
            {
                vLedDutySet(ptypeLed, ((st_uiLedSosCnt / (LED_FLASH_SLOW_PERIOD / 2)) & 1) ? 1.0f : 0.0f);
            }
            else if(st_uiLedSosCnt < ((LED_FLASH_FAST_PERIOD + LED_FLASH_SLOW_PERIOD + LED_FLASH_FAST_PERIOD) * 3))
            {
                vLedDutySet(ptypeLed, ((st_uiLedSosCnt / (LED_FLASH_FAST_PERIOD / 2)) & 1) ? 1.0f : 0.0f);
            }
            else
            {
                vLedDutySet(ptypeLed, 0.0f);
            }
            break;

        default : break;
    }
}

/*
 * Return:      void
 * Parameters:  void
 * Description: LED心跳计数 + 呼吸状态机
 */
static void vLedIncTick(void)
{
    /* 呼吸方向 */
    volatile static int8_t st_cDirection = 1;

    /* 所有LED使用同一个状态机变量，以方便实现一致的闪烁步调 */
    st_uiLedTickCnt += LED_EXECUTION_PERIOD;
    st_uiLedSosCnt += LED_EXECUTION_PERIOD;
    st_uiLedSosCnt %= ((LED_FLASH_FAST_PERIOD + LED_FLASH_SLOW_PERIOD + LED_FLASH_FAST_PERIOD) * 3 + LED_SOS_DELAY_PERIOD);

    /* 更新呼吸模式占空比 */
    {
        st_sGrade += st_cDirection;
        /* 之所以把档位设置的比 0 小一点，是为了在呼吸状态时，LED能够彻底熄灭一段时间 */
        st_cDirection = (st_sGrade >= 125) ? -1 : ((st_sGrade <= -25) ? 1 : st_cDirection);
    }
}

/* 20ms调用一次 */
void vLedMachine(void)
{
    LedType *ptypeLedHandle = NULL;
    int8_t i = 0, cTotal = 0;

    /* 更新1次计数值 */
    vLedIncTick();

    ptypeLedHandle = (LedType *)(&st_typeLedInfo);

    /* 扫描所有的led状态 */
    cTotal = sizeof(LedInfoType) / sizeof(LedType);
    for(i = 0; i < cTotal; ++i)
    {
        vLedStateMachine(ptypeLedHandle++);
    }
}

LedInfoType *ptypeLedGetInfo(void)
{
    return &st_typeLedInfo;
}
